use std::{
    error::Error,
    fmt::{Debug, Display, Formatter, Result as FmtResult},
    io::Error as IoError,
    net::AddrParseError,
    result::Result as StdResult,
};

#[derive(Debug)]
pub enum QuicRpcWrapError {
    AddrParse(AddrParseError),
    #[cfg(feature = "iroh")]
    Anyhow(anyhow::Error),
    BadSink,
    #[cfg(feature = "hyper")]
    Hyper(hyper::Error),
    #[cfg(feature = "hyper")]
    InvalidUri(hyper::http::uri::InvalidUri),
    Io(IoError),
    #[cfg(any(feature = "iroh", feature = "quinn"))]
    NoInitialCipherSuite(iroh_quinn::crypto::rustls::NoInitialCipherSuite),
    #[cfg(any(feature = "iroh", feature = "quinn"))]
    Rcgen(rcgen::Error),
    ResultAlreadyTakenAway,
    Recv(String),
    Request,
    Response(String),
    Send(String),
    #[cfg(any(feature = "iroh", feature = "quinn"))]
    Rustls(iroh_quinn::rustls::Error),
}

impl Display for QuicRpcWrapError {
    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
        write!(f, "QuicRpcWrapError: ")?;
        match self {
            Self::AddrParse(e) => Display::fmt(e, f),
            #[cfg(feature = "iroh")]
            Self::Anyhow(e) => Display::fmt(e, f),
            Self::BadSink => Display::fmt("BadSink", f),
            #[cfg(feature = "hyper")]
            Self::Hyper(e) => Display::fmt(e, f),
            #[cfg(feature = "hyper")]
            Self::InvalidUri(e) => Display::fmt(e, f),
            Self::Io(e) => Display::fmt(e, f),
            #[cfg(any(feature = "iroh", feature = "quinn"))]
            Self::NoInitialCipherSuite(e) => Display::fmt(e, f),
            #[cfg(any(feature = "iroh", feature = "quinn"))]
            Self::Rcgen(e) => Display::fmt(e, f),
            Self::ResultAlreadyTakenAway => Display::fmt("ResultAlreadyTakenAway", f),
            Self::Recv(e) => Display::fmt(e, f),
            Self::Request => Display::fmt("Request", f),
            Self::Send(e) => Display::fmt(e, f),
            Self::Response(e) => Display::fmt(e, f),
            #[cfg(any(feature = "iroh", feature = "quinn"))]
            Self::Rustls(e) => Display::fmt(e, f),
        }
    }
}

impl Error for QuicRpcWrapError {}

impl From<IoError> for QuicRpcWrapError {
    fn from(value: IoError) -> Self {
        Self::Io(value)
    }
}

impl From<AddrParseError> for QuicRpcWrapError {
    fn from(value: AddrParseError) -> Self {
        Self::AddrParse(value)
    }
}

#[cfg(feature = "hyper")]
impl From<hyper::Error> for QuicRpcWrapError {
    fn from(value: hyper::Error) -> Self {
        Self::Hyper(value)
    }
}

#[cfg(feature = "hyper")]
impl From<hyper::http::uri::InvalidUri> for QuicRpcWrapError {
    fn from(value: hyper::http::uri::InvalidUri) -> Self {
        Self::InvalidUri(value)
    }
}

#[cfg(any(feature = "iroh", feature = "quinn"))]
impl From<rcgen::Error> for QuicRpcWrapError {
    fn from(value: rcgen::Error) -> Self {
        Self::Rcgen(value)
    }
}

#[cfg(any(feature = "iroh", feature = "quinn"))]
impl From<iroh_quinn::rustls::Error> for QuicRpcWrapError {
    fn from(value: iroh_quinn::rustls::Error) -> Self {
        Self::Rustls(value)
    }
}

#[cfg(any(feature = "iroh", feature = "quinn"))]
impl From<iroh_quinn::crypto::rustls::NoInitialCipherSuite> for QuicRpcWrapError {
    fn from(value: iroh_quinn::crypto::rustls::NoInitialCipherSuite) -> Self {
        Self::NoInitialCipherSuite(value)
    }
}

#[cfg(feature = "iroh")]
impl From<anyhow::Error> for QuicRpcWrapError {
    fn from(value: anyhow::Error) -> Self {
        Self::Anyhow(value)
    }
}

pub type Result<R> = StdResult<R, QuicRpcWrapError>;
